import { message } from 'antd'
import axios, {
  AxiosError,
  AxiosInstance,
  AxiosRequestConfig,
  AxiosResponse,
  InternalAxiosRequestConfig,
} from 'axios'

import { ResultData } from '@/api/interface'
import { showFullScreenLoading, tryHideFullScreenLoading } from '@/components/Loading/fullScreen'
import { ResultEnum } from '@/enums/httpEnum'
import store from '@/store'
import { get } from '@/utils/storage'

import { AxiosCanceler } from './helper/axiosCancel'
import locale from './locale'

export interface CustomAxiosRequestConfig extends InternalAxiosRequestConfig {
  loading?: boolean
  cancel?: boolean
}

const getLocale = (key: keyof (typeof locale)['en-US']) => {
  const lang: keyof typeof locale = (get('arco-lang') as keyof typeof locale) || 'ar-EG'
  return locale[lang][key]
}

const config = {
  // The default address request address
  baseURL: process.env.NEXT_PUBLIC_BASE,
  // timeout
  timeout: ResultEnum.TIMEOUT as number,
  withCredentials: false,
}

const whiteList = ['/wajib/entity/violation/tasks/notice']

const axiosCanceler = new AxiosCanceler()
const key = 'updatable'
class RequestHttp {
  service: AxiosInstance
  public constructor(config: AxiosRequestConfig) {
    // instantiation
    this.service = axios.create(config)

    /**
     * @description request interceptor
     * Client sends request -> [request interceptor] -> server
     * token verification (JWT): Accept the token returned by the server and store it in redux/local storage
     */
    this.service.interceptors.request.use(
      (config: CustomAxiosRequestConfig) => {
        // cancel request
        config.cancel ?? (config.cancel = true)
        config.cancel && axiosCanceler.addPending(config)
        // request loading
        config.loading && showFullScreenLoading()
        if (config.headers && typeof config.headers.set === 'function') {
          const token = store.getState().token ? `Bearer ${store.getState().token}` : ''
          const websiteToken = store.getState().websiteToken ?? ''
          config.headers.set('Authorization', token)
          config.headers.set('token', websiteToken)
        }
        return config
      },
      (error: AxiosError) => {
        return Promise.reject(error)
      }
    )

    /**
     * @description response interceptor
     *  The server returns the information -> [intercept unified processing] -> the client JS gets the information
     */
    this.service.interceptors.response.use(
      (response: AxiosResponse & { config: CustomAxiosRequestConfig }) => {
        const { data, config } = response
        axiosCanceler.removePending(config)
        config.loading && tryHideFullScreenLoading()
        // login failure
        if (data.code == 1023 || data.code == 1001 || data.code == 1020) {
          type ErrorCode = '1001' | '1012' | '1020' | '1023'
          const msg = getLocale(`request.error.${data.code}` as `request.error.${ErrorCode}`)
          message.error({
            content: msg,
            key,
          })
          store.dispatch({
            type: 'reset',
          })
          return Promise.reject(data)
        }
        if (data.code && data.code !== 200) {
          message.error({ content: data.msg, key })
          return Promise.reject(data.msg || 'Error')
        }
        // Successful request (no need to handle failure logic on the page unless there are special circumstances)
        return data
      },
      (error: AxiosError & { config: CustomAxiosRequestConfig }) => {
        const { message: msg, config } = error
        axiosCanceler.removePending(config!)
        config.loading && tryHideFullScreenLoading()
        // Request timeout && network error judged separately, no response
        const url = config?.url ?? ''
        if (error.message.indexOf('timeout') !== -1 && !whiteList.includes(url)) {
          message.error({ content: getLocale(`request.error.timeout`), key })
        }
        if (error.message.indexOf('Network Error') !== -1 && !whiteList.includes(url)) {
          message.error({ content: getLocale(`request.error.networkErr`), key })
        }
        return Promise.reject(msg || 'Error')
      }
    )
  }

  /**
   * @description Common request method encapsulation
   */
  get<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.get(url, { params, ..._object })
  }
  post<T>(url: string, params?: object | string, _object = {}): Promise<ResultData<T>> {
    return this.service.post(url, params, _object)
  }
  put<T>(url: string, params?: object, _object = {}): Promise<ResultData<T>> {
    return this.service.put(url, params, _object)
  }
  delete<T>(url: string, params?: any, _object = {}): Promise<ResultData<T>> {
    return this.service.delete(url, { params, ..._object })
  }
  download(url: string, params?: object, _object = {}): Promise<BlobPart> {
    return this.service.post(url, params, { ..._object, responseType: 'blob' })
  }
}

export default new RequestHttp(config)
